/*
This software is distributed under the Simplified BSD License:

Copyright (c) 2008, Chris Venter <chris.venter@gmail.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, 
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, 
    	this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, 
    	this list of conditions and the following disclaimer in the documentation 
    	and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
DAMAGE.
*/
#ifndef GENERIK_THREADS_H
#define GENERIK_THREADS_H

/** \file threads.h
This file provides convenience wrappers for the pthreads library.

\todo MORE ERROR HANDLING!!! 
*/

#include <errno.h>
#include <pthread.h>
#include <list>
#include <algorithm>

#include "tracing.h"
#include <iostream>
#include <sstream>

namespace generik
{
	
	// *****************************************************
	// threads
	// *****************************************************
	
	/** handle a thread creation return code
	\todo decide which errors need a return code, which can be safely ignored (?), and which need an 
	exception thrown.
	ignored - informational
	return code - non-critical
	exception - critical
	*/
	inline void handle_pthread_create_error(int result)
	{
		GENERIK_TRACER;
		switch (result)
		{
			case 0:
				// no error
				break;
			case EAGAIN:
				// non-critical - except in our case, because we do this in a constructor?
				GENERIK_ERROR("EAGAIN: lack of resources");
				break;
			case EINVAL:
				// critical
				GENERIK_ERROR("EINVAL: invalid attr");
				break;
			case EPERM:
				// critical
				GENERIK_ERROR("EPERM: no permission");
				break;
			default:
				// critical
				GENERIK_ERROR("Unknown error");
		}
	}
	
	/** handle a miscelaneous pthread return code
	This handles return codes from join, detach, etc. ?
	\todo decide which errors need a return code, which can be safely ignored (?), and which need an 
	exception thrown.
	*/
	inline void handle_pthread_misc_error(int result)
	{
		GENERIK_TRACER;
		switch (result)
		{
			case 0:
				// no error
				break;
			case EINVAL:
				// non-critical - runtime; critical - logic error?
				GENERIK_ERROR("EINVAL: not a joinable thread");
				break;
			case ESRCH:
				// non-critical - runtime; critical - logic error?
				GENERIK_ERROR("ESRCH: no such thread");
				break;
			case EDEADLK:
				// critical - can't go anywhere! programming/logic error?
				GENERIK_ERROR("EDEADLK: deadlock detected");
				break;
			default:
				// critical
				GENERIK_ERROR("Unknown error");
		}
	}
	
	/** handle a cond_wait return code
	\todo decide which errors need a return code, which can be safely ignored (?), and which need an 
	exception thrown.
	*/
	inline void handle_cond_wait_error(int result)
	{
		GENERIK_TRACER;
		switch (result)
		{
			case 0:
				// no error
				break;
			case EINVAL:
				// critical - logic/programming error
				GENERIK_ERROR("EINVAL: invalid value, differing mutexes or unowned mutex used");
				break;
			default:
				GENERIK_ERROR("Unknown error");
		}
	}
	
	/** handle a mutex lock/trylock/unlock return code
	\todo decide which errors need a return code, which can be safely ignored (?), and which need an 
	exception thrown.
	*/
	inline void handle_mutex_lock_error(int result)
	{
		GENERIK_TRACER;
		switch(result)
		{
			case 0:
				// no error
				break;
			case EINVAL:
				// critical - logic error
				GENERIK_ERROR("EINVAL: uninitialized mutex; mutex priority conflict");
				break;
			case EBUSY:
				// informational, return this code!
				GENERIK_TRACE("BUSY: mutex is already locked");
				break;
			case EAGAIN:
				// non-critical, return the code
				GENERIK_ERROR("EAGAIN: recursive lock count exceeded");
				break;
			case EDEADLK:
				// critical error - logic error, runtime error?
				GENERIK_ERROR("EDEADLK: current thread already locks mutex");
				break;
			case EPERM:
				// critical error?
				GENERIK_ERROR("EPERM: current thread does not own mutex");
				break;
			default:
				GENERIK_ERROR("Unknown error");
		}
	}
	
	/** A convenience construct for a thread
	This object "wraps" a thread, in the loosest sense of the word.
	What it does is wrap the code which is to be run inside the thread, as well
	as any data the thread might want to access in a convenient object, along with
	the thread's id, and some control functions.
	
	These control functions for a specific thread are, however, available to any other 
	thread which has a reference to the specific thread's wrapper object.
	
	Therefore some functions have been marked external, and some internal.
	Some may be used either externally or internally, as long as one remembers that
	the object is NOT the thread!
	
	Especially where the lifetime of the thread or the object is compared;
	the thread may outlive the object (if someone else destroys it) or the thread
	may be outlived by the object (if someone else cancels it).
	*/
	struct thread
	{
		pthread_t tid;
		
		virtual void* run() = 0;
		
		static void* run(void* object)
		{
			thread* t = static_cast<thread*>(object);
			return t->run();
		}
		
		// external function
		void execute()
		{
			GENERIK_TRACER;
			
			int result = pthread_create(&tid, 0, run, this);
			if (result != 0) handle_pthread_create_error(result);
		}
		
		// internal/external function
		void detach()
		{
			GENERIK_TRACER;
			int result = pthread_detach(tid);
			if (result != 0) handle_pthread_misc_error(result);
		}
		
		pthread_t self()
		{
			return tid;
		}
		
		// external function
		void* join()
		{
			GENERIK_TRACER;
			pthread_t t = pthread_self();
			unsigned int p = *((unsigned int*)&t);
			unsigned int p_ = *((unsigned int*)&tid);
			GENERIK_TRACE("thread "<<p<<" is joining with "<<p_);
			void* retval = 0;
			int result = pthread_join(tid, &retval);
			if (result != 0) handle_pthread_misc_error(result);
			return retval;
		}
		
		// internal/external function
		void exitthread(void* retval = 0)
		{
			GENERIK_TRACER;
			pthread_exit(retval);
			// should not reach here! do an implicit throw?
		}
		
		// external (internal, but what's the point?)
		void cancel()
		{
			GENERIK_TRACER;
			pthread_t t = pthread_self();
			unsigned int p = *((unsigned int*)&t);
			unsigned int p_ = *((unsigned int*)&tid);
			GENERIK_TRACE("thread "<<p<<" is cancelling "<<p_);
			int result = pthread_cancel(tid);
			if (result != 0) handle_pthread_misc_error(result);
		}
		
		// internal/external
		void stop()
		{
			GENERIK_TRACER;
			cancel();
			join();
		}
		
		// internal
		inline void yield()
		{
			GENERIK_TRACER;
#ifndef __CYGWIN__
#ifndef __MINGW32__
			pthread_yield(); // not implemented in cygwin or in mingw32
#endif
#endif
			// use sched_yield() instead?
		}
		
		inline void kill(int sig)
		{
			GENERIK_TRACER;
			pthread_kill(tid, sig);
		}
	};
	
	// *****************************************************
	// mutexes
	// *****************************************************
	
	/** A wrapper for the mutex_attr structure. It has a default
	constructor which initialises the structure with default values,
	and a destructor which disposes of the structure safely.
	*/
	struct mutex_attr
	{
		pthread_mutexattr_t _attr;
#ifdef GENERIK_TRACING
		mutex_attr(int options = PTHREAD_MUTEX_ERRORCHECK)
#else
		mutex_attr(int options = 0)
#endif
		{
			GENERIK_TRACER;
			GENERIK_TRACE_VALUE(options);
			int result;
			result = pthread_mutexattr_init(&_attr);
			result = pthread_mutexattr_settype(&_attr, options);
		}
		pthread_mutexattr_t* operator()()
		{
			return &_attr;
		}
		~mutex_attr()
		{
			GENERIK_TRACER;
			pthread_mutexattr_destroy(&_attr);
		}
	};
	
	/** A wrapper for the mutex structure.
	It takes care of the initialisation and destruction of the mutex structure.
	*/
	struct mutex
	{
		std::string _name;
		pthread_mutex_t _mutex;
#ifdef GENERIK_TRACING
// if tracing is enabled, make the default mutex an error-checking mutex
		mutex(const std::string& name, int options = PTHREAD_MUTEX_ERRORCHECK)
#else
// otherwise use the default mutex type
		mutex(const std::string& name, int options = 0)
#endif
		: _name(name)
		{
			GENERIK_TRACER;
			mutex_attr attr(options);
			pthread_mutex_init(&_mutex, attr());
		}
		pthread_mutex_t* operator()()
		{
			return &_mutex;
		}
		/// destructor
		/** destroys the mutex. Application code must ensure that no locks on this
		mutex are held at the time of its destruction.
		\todo Can this be checked for?
		*/
		virtual ~mutex()
		{
			GENERIK_TRACER;
			/*
			if (!trylock())
			{
				GENERIK_ERROR("could not lock mutex "<<(void*)&_mutex<<": "<<_name<<" prior to destroying it");
				throw;
			}
			*/
			GENERIK_TRACE("locking mutex "<<(void*)&_mutex<<": "<<_name<<" prior to destroying it");
			pthread_mutex_lock(&_mutex);
			pthread_mutex_unlock(&_mutex);
			pthread_mutex_destroy(&_mutex);
		}
		void lock()
		{
			REM_GENERIK_TRACER;
			//
			GENERIK_INLINE("LOCKING    "<<(void*)&_mutex<<": "<<_name);
			//
			int result = pthread_mutex_lock(&_mutex);
			if (result != 0) handle_mutex_lock_error(result);
			//
			GENERIK_INLINE("LOCKED     "<<(void*)&_mutex<<": "<<_name);
		}
		bool trylock()
		{
			REM_GENERIK_TRACER;
			// returns true if the lock could be obtained
			//
			GENERIK_INLINE("TRYLOCKING "<<(void*)&_mutex<<": "<<_name);
			//
			int result = pthread_mutex_trylock(&_mutex);
			if (result != 0) handle_mutex_lock_error(result);
			
			GENERIK_INLINE("TRYLOCKED  "<<(void*)&_mutex<<": "<<_name);
			return result == 0;
		}
		void unlock()
		{
			REM_GENERIK_TRACER;
			//
			GENERIK_INLINE("UNLOCKING  "<<(void*)&_mutex<<": "<<_name);
			//
			int result = pthread_mutex_unlock(&_mutex);
			if (result != 0) handle_mutex_lock_error(result);
			//
			GENERIK_INLINE("UNLOCKED   "<<(void*)&_mutex<<": "<<_name);
		}
	};

	/** A recursive mutex object
	*/
	struct rec_mutex
	: mutex
	{
		rec_mutex(const std::string& name, int options = 0)
		: mutex(name, options | PTHREAD_MUTEX_RECURSIVE)
		{
		}
	};

	// *****************************************************
	// locks
	// *****************************************************
	
	/** A scoped guard object which locks a mutex upon construction
	and unlocks it upon destruction.
	*/
	struct lock
	{
		mutex& _mutex;
		lock(mutex& mutex_in)
		: _mutex(mutex_in)
		{
			//GENERIK_TRACER;
			_mutex.lock();
		}
		~lock()
		{
			//GENERIK_TRACER;
			_mutex.unlock();
		}
	};
	/** A scoped guard object which trylocks a mutex upon construction
	and unlocks it upon destruction.
	*/
	struct trylock
	{
		mutex& _mutex;
		bool _result;
		trylock(mutex& mutex_in)
		: _mutex(mutex_in)
		{
			//GENERIK_TRACER;
			_result = _mutex.trylock();
		}
		bool islocked()
		{
			return _result;
		}
		~trylock()
		{
			//GENERIK_TRACER;
			_mutex.unlock();
		}
	};

/** \def GENERIK_THREAD_LOCK
A convenience macro which creates a mutex lock object.
*/
#define GENERIK_THREAD_LOCK(mutex) \
	generik::lock lock_##__LINE__(mutex)

/** \def GENERIK_TRY_LOCK
A convenience macro which creates a mutex trylock object.
*/
#define GENERIK_TRY_LOCK(mutex) \
	generik::trylock lock_##__LINE__(mutex); if (!lock_##__LINE__.islocked()) return

	// *****************************************************
	// conditions
	// *****************************************************
	
	/** A wrapper for the condition stucture.
	*/
	struct condition
	//: mutex
	{
		mutex& _mutex;
		pthread_cond_t _cond;
		
		condition(mutex& mutex_in, pthread_condattr_t* options_in = 0)
		: _mutex(mutex_in)
		{
			GENERIK_TRACER;
			pthread_cond_init(&_cond, options_in);
		}
		virtual ~condition()
		{
			GENERIK_TRACER;
			pthread_cond_destroy(&_cond);
		}
		
		static void cleanup(void* object)
		{
			GENERIK_TRACER;
			condition* c = (condition*)object;
			c->unlock();
		}
		
		void wait_()
		{
			REM_GENERIK_TRACER;
			pthread_cleanup_push(cleanup, this);
			int result = pthread_cond_wait(&_cond, &_mutex._mutex);
			if (result != 0) handle_cond_wait_error(result);
			pthread_cleanup_pop(0);
		}
		
		void lock()
		{
			_mutex.lock();
		}
		void unlock()
		{
			_mutex.unlock();
		}
		
		/*
		as far as I can tell, wait() should NOT release the lock.
		or actually, it does not matter?
		*/
		void wait()
		{
			GENERIK_TRACER;
			lock();
			wait_();
			//unlock();
			//pthread_cond_wait(&_cond, &_mutex);
		}
		
		void wait_locked()
		{
			GENERIK_TRACER;
			wait_();
		}
		
		void signal()
		{
			GENERIK_TRACER;
			lock();
			pthread_cond_signal(&_cond);
			unlock();
		}
		void signal_locked()
		{
			GENERIK_TRACER;
			pthread_cond_signal(&_cond);
			unlock();
		}
		
		void broadcast()
		{
			GENERIK_TRACER;
			lock();
			pthread_cond_broadcast(&_cond);
			unlock();
		}
		void broadcast_locked()
		{
			GENERIK_TRACER;
			pthread_cond_broadcast(&_cond);
			unlock();
		}
	};
	
}

#endif

